<html>
    <head>
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
        <title>数字记忆</title>
        <style>
            html, body {
                background-color: rgba(0, 0, 0, 0.5);
                height: 100%;
                padding: 0;
                margin: 0;
            }
            .canvas_wrap {
                position: fixed;
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%);
                background-color: rgba(255, 255, 255, 1);
                padding: 10px;
                cursor: pointer;
            }

            #tools {
                display: none;

                position: fixed;
                z-index: 2;
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%);
                background-color: rgba(255, 255, 255, 1);
                padding: 40px;
            }
            #mask {
                display: none;

                position: fixed;
                left: 0%;
                top: 0%;
                width: 100%;
                height: 100%;
                z-index: 1;

                background-color: rgba(0, 0, 0, 0.5);
            }
            #tools .row {
                display: inline-block;
            }
            @media screen and (max-width: 850px)  {
                #tools .row {
                    padding: 5px;
                }
                /* #save {
                    display: none;
                } */
            }
        </style>
    </head>
    <body>
        <div class="canvas_wrap">
            <canvas id="canvas" width="500" height="500"></canvas>
        </div>

        <div id="mask"></div>
        <div id="tools">
            <div class="row">
                <label>宫格数</label>
                <select id="num"></select>
            </div>
            <div class="row">
                <label>类型</label>
                <select id="type">
                    <option value="">数字</option>
                    <option value="hanzi">汉字</option>
                </select>
            </div>
            <div class="row">
                <button id="create">重新生成</button>
            </div>
            <div class="row">
                <button id="save">保存当前图片</button>
            </div>
        </div>
        <script>
            let font_list = `小学一年级常用汉字生字　　一、二、三、十、木、禾、上、下、土、个、八、入、大、天、人、火、文、六、七、儿、九、无、口、日、中、了、子、门、月、不、开、四、五、目、耳、头、米、见、白、田、电、也、长、山、出、飞、马、鸟、云、公、车、牛、羊、小、少、巾、牙、尺、毛、卜、又、心、风、力、手、水、广、升、足、走、方、半、巴、业、本、平、书、自、已、东、西、回、片、皮、生、里、果、几、用、鱼、今、正、雨、两、瓜、衣、来、年、左、右、万、百、丁、齐、冬、说、友、话、春、朋、高、你、绿、们、花、红、草、爷、亲、节、的、岁、行、古、处、声、知、多、忙、洗、真、认、父、扫、母、爸、写、全、完、关、家、看、笑、着、兴、画、会、妈、合、奶、放、午、收、女、气、太、早、去、亮、和、李、语、秀、千、香、听、远、唱、定、连、向、以、更、后、意、主、总、先、起、干、明、赶、净、同、专、工、才、级、队、蚂、蚁、前、房、空、网、诗、黄、林、闭、童、立、是、我、朵、叶、美、机、她、过、他、时、送、让、吗、往、吧、得、虫、很、河、借、姐、呢、呀、哪、谁、凉、怕、量、跟、最、园、脸、因、阳、为、光、可、法、石、找、办、许、别、那、到、都、吓、叫、再、做、象、点、像、照、沙、海、桥、军、竹、苗、井、面、乡、忘、想、念、王、这、从、进、边、道、贝、男、原、爱、虾、跑、吹、乐、地、老、快、师、短、淡、对、热、冷、情、拉、活、把、种、给、吃、练、学、习、非、苦、常、问、伴、间、共、伙、汽、分、要、没、孩、位、选、北、湖、南、秋、江、只、帮、星、请、雪、就、球、跳、玩、桃、树、刚、兰、座、各、带、坐、急、名、发、成、动、晚、新、有、么、在、变、什、条。　　小学二年级常用汉字生字　　宜、实、色、华、谷、金、尽、层、丰、壮、波、浪、灯、作、字、苹、丽、劳、尤、其、区、巨、它、安、块、站、已、甲、豆、识、纷、经、如、好、娃、洼、于、首、枝、枫、记、刘、胡、戏、棋、钢、观、弹、琴、养、休、伸、甜、歌、院、除、息、您、牵、困、员、青、宁、室、样、校、切、教、响、班、欠、元、包、钟、叹、哈、迟、闹、及、身、仔、细、次、外、计、怦、礼、加、夕、与、川、州、台、争、民、族、亿、洁、欢、祖、旗、帜、庆、曲、央、交、市、旁、优、阴、坛、城、国、图、申、匹、互、京、泪、洋、拥、抱、相、扬、讲、打、指、接、惊、故、侯、奇、寸、落、补、拔、功、助、取、所、信、沿、拾、际、蛙、错、答、还、言、每、治、棵、挂、哇、怪、慢、怎、思、穿、弯、比、服、浅、漂、啦、啊、夫、、示、号、汗、伤、吸、极、串、免、告、诉、狐、狸、猴、颗、斤、折、挑、根、独、满、容、易、采、背、板、椅、但、傍、清、消、由、术、吐、注、课、铅、笔、桌、景、拿、坏、松、扎、抓、祝、福、句、幸、之、令、布、直、当、第、现、期、轮、路、丑、永、饥、饱、温、贫、富、户、亚、角、周、床、病、始、张、寻、哭、良、食、双、体、操、场、份、粉、昨、晴、姑、娘、妹、读、舟、乘、音、客、何、汪、丛、牢、拍、护、保、物、鸡、猫、羽、领、捉、理、跃、蹦、灵、晨、失、觉、扔、掉、眼、睛、纸、船、久、乎、至、死、腰、捡、粒、被、并、夜、喜、重、味、轻、刻、群、卫、运、宇、宙、航、舰、冲、晒、池、浮、灾、害、黑、器、岸、纹、洞、影、倒、游、圆、围、杯、件、住、须、能、飘、必、事、历、史、灭、克、化、代、孙、植、厂、产、介、农、科、技、纺、织、脱、冻、溪、棉、探、摇、野、躲、解、未、追、店、枯、徐、烧、荣、菜、宿、冈、世、界、轰、笋、芽、喊、呼、唤、弟、哥、骨、抽、拐、浇、终、静、躺、谢、渐、微、瓦、泉、然、结、股、脆、塔、杜、鹃、冒、雷、迈、迷、迹、叔、锋、滴、洒、泥、泞、扑、托、摸、利、铃、弱、末、芬、芳、夏、应、该、岛、展、建、纱、环、绕、胜、隐、约、省、茂、盛、吾、季、留、杏、密、蜜、坡、搭、摘、钉、沟、龙、寿、泼、敬、脚、凤、束、府、夺、扮、伟、够、恩、柏、特、鲜、度、凰、色、勾、单、宫、雄、烁、辉、另、题、漫、哄、骗、尔、仍、便、票、式、且、煌、志、提、朗、喝、刀、求、使、英、整、而、丹、乌、显、丝、眨、陈、斜、含、炉、鸣、银、泊、柳、艺、忽、杆、涛、转、吴、窗、岭、绝、烟、流、垂、乱、压、越、彩、蝉、蛛、岩、趴、刨、陆、质、厚、沉、逃、阵、虹、蜘、蛛、册、宝、印、埋、铁、底、导、盏、稠、针、稀、碰、兄、商、挤、决、钱、批、报、破、碎、滑、继、续、封、骄、傲、拎、桶、停、聪、胳、膊、甸、晃、荡、叭、玲、狗、糟、梯、楼、肯、脑、筋、讶、谈、望、算、此、桩、肥、灰、讨、厌、冰、蛋、壳、鸭、欺、负、鹅、翅、膀、勺、斗、玉、组、珍、珠、数、钻、研、睡、距、离、油、检、查、团、斥、责、炎、夸、奖、亡、肉、耐、谜、传、染、类、严、寒。　　小学三年级常用汉字生字　　坪、坝、戴、招、蝴、蝶、孔、雀、舞、铜、粗、尾、要、装、劲、绒、朝、些、钓、察、瓣、拢、掌、趣、爬、峰、顶、似、苍、仰、咱、奋、辫、勇、居、郊、散、步、胸、脯、渣、或、者、敢、惜、低、诚、基、突、按、摆、弄、准、备、侧、胶、卷、辆、秘、杂、社、著、藏、悄、闪、坑、臣、推、旅、考、秦、纪、遗、究、震、促、深、忆、异、逢、佳、倍、遥、遍、插、精、希、却、依、拼、命、奔、村、抖、丧、磨、坊、扇、枚、邮、爽、柿、仙、梨、菠、萝、粮、紧、杨、艳、内、梦、醒、苏、湿、娇、嫩、强、适、昆、播、修、致、论、试、验、袋、证、概、减、阻、测、括、确、误、途、超、堂、镜、闲、待、阅、腿、随、调、简、拜、访、具、闻、尘、仆、纳、闷、丘、迎、等、止、境、授、品、暗、降、丈、肢、肌、肤、辽、阔、血、液、滋、润、创、造、县、设、参、部、横、跨、举、击、坚、固、栏、案、爪、贵、断、楚、孤、帆、蓝、懒、披、划、威、武、拣、颜、形、状、渔、料、辈、汇、欣、赏、映、挡、视、线、浸、献、药、村、软、刮、舌、矛、盾、集、持、般、架、龟、攻、炮、坦、战、神、兵、退、挖、鞋、斧、锯、免、屋、抢、难、初、管、敌、阶、懂、陶、谦、虚、嘴、恼、怒、吵、感、荒、捧、朴、素、值、受、愿、姿、势、投、况、吞、烈、绪、述、普、通、鼓、励、育、瓶、系、绳、茶、危、险、顺、俩、索、激、堵、获、矛、担、宽、裕、买、猜、糖、即、卡、盼、仁、贴、筝、鹰、端、稳、嚷、橘、墨、斑、闹、宇、宙、荡、衬、赛、拨、警、惕、膝、毫、套、倾、探、扎、搂、贯、局、昼、耘、未、耕、织、桑、蓬、稚、纶、莓、苔、叮、嘱、排、而、幅、却、哈、审、肃、晌、悦、悉、诲、例、芝、卵、匀、寸、呆、增、隔、壁、搬、烛、慈、肩、捧、匠、伟、砌、跨、创、既、毁、固、且、案、智、慧、登、阶、厅、礼、席、灿、烂、党、描、敞、椅、议、宾、翠、秆、绣、腹、赤、衫、疾、泛、泡、脱、锐、晃、饲、贪、狭、桂、刺、软、触、滑、唇、汁、津、腐、虽、味、亭、移、泊、愁、旷、萤、簇、烧、喂、盈、寿、合、茄、跪、猛、庙、蹲、镇、须、揉、挨、挤、之、莲、胀、姿、仿、裳、翩、随、涛、依、否、窃、私、汪、类、镜、煤、属、珍、诊、职、扶、除、疑、效、缺、况、编、尝、传、延、庆、扬、叠、涌、股、螺、旋、桨、掠、励、顾、融 腾、掩、盗、铛、偷、碰、株、待、窜、撞、桩、捡、锄、魏、箭、猎、雁、弦、悲、惨、愈、痛。　　小学四年级常用汉字生字　　歇、联、雕、厘、甚、鼻、藏、概、淘、楚、侵、衅、驻、抗、鲁、绍、馆、戒、段、务、奔、厉、弛、忧、哀、持、慰、谜、梭、狂、赢、益、若、凳、锅、替、抄、印、嗓、捆、俯、投、轰、隆、爆、叨、役、营、占、攻、枪、怒、艰、牺、顽、暴、臂、规、膛、仇、鹭、含、岭、帝、辞、陵、猿、啼、殿、廊、漆、栏、昆、爽、阁、辉、煌、葱、朱、痕、堤、哄、驾、耀、亩、丈、拇、镰、笋、伏、丘、折、缝、抚、糖、配、饮、钟、程、闯、训、选、择、验、资、源、宋、拴、陷、论、尚 潜、舱、绳、绑、铲、拖、亚、匣、挖、鞋、锯、避、倍、庐、瀑、炉、潮、据、罩、薄、闷、沸、踮、犹、浩、崩、霎、余、恢、涨、虎、隙、拂、漾、柄、曲、逐、瞧、型、陈、搏、促、企、犯、罪、殊、夹、即、碎、改、集、付、启、抛、剧、翼、纵、跃、挣、距、丧、纽、抉、曾、践、获、堡、瘦、纠、掏、乞、轧、躺、饥、质、趵、纯、乏、藻、扁、崇、峻、蜿、蜒、砖、屯、垒、峭、凝、魄、惠、莺、郭、酒、哺、络、绎、桶、吱、旬、拐、缸、酬、俺、歉、仅、治、坦、谓、梯、坚、茏、览、械、愧、疚、嫌、辛、嗅、奈、绒、躯、拯、幼、浑、哑、搏、庞、愣、虑、凭、痒、稿、腔、要、摔、跌、胆、辟、遭、殃、责、倔、荒、忍、惫、牵、翘、鬼、吻、递、嘛、港、邦、芜、巫、婆、逼、扮、旱、绸、褂、徒、烦、饶、渠、灌、溉、吩、咐、茅、榨、价、榴、慕、矮、浙、罗、呈、郁、聚、适、昏、稍、额、估、损、皇、组、般、征、苏、宏、唐、凡、统、销、略、奉、执、维、予、素、浸、凯、遗、硕、贡、圣、协、吁、妻、充、孟、帆、唯、偶、鬓、衰、查、克、迫、睫、垫、泣、呜、咽、拳、竭、亿、抵、钢、兽、繁、殖、蔬、炭、蒸、杀、菌、预、疗、捷、购、屏、访、辅、邮、贺、羡、宜、恋、妙。　　小学五年级常用汉字生字　　范、刹、镶、裹、泻、镀、润、杰、截、溢、则、燃、缘、溜、货、奏、衡、诵、杖、超、肌、拘、耽、误、哲、仪、悼、逝、餐、枣、搞、冠、骂、嚼、悟、摊、奥、咳、嗽、拄、槐、耐、挽、饰、腮、洲、陪、检、阅、矫、锁、暂、糕、阻、谊、捣、谣、侦、混、吵、耗、嫂、勒、骏、限、鞭、驰、蹄、茶、貌、杯、跤、尼、艇、叉、耸、寂、梁、寇、晋、挥、葛、尸、悬、崖、斩、磨、罢、豪、屈、沃、刘、龄、匪、拒、醉、剂、施、哼、晕、勉、堪、承、吟、残、瑟、捏、扭、胯、郑、拜 租、允、厨、颈、缚、稻、贼、畜、鲸、肢、滤、吨、肺、判、胎、宅、蔽、弃、慎、址、掘、搜、骤、糙、朴、燥、钳、嘻、舀、掺、逗、棍、逮、萝、卜、筷、哗、哇、托、批、糟、蹋、尔、肆、痰、核、呻、究、律、俊、俏、拢、添、沾、倦、谱、符、塞、笠、蓑、戈、晰、介、疆、萎、汲、赖、旦、番、锻、炼、雅、勃、艘、航、桅、撕、唬、龇、咧、鸥、瞄、莱、茵、幽、券、蜡、瞎、陌、盲、键、粼、缕、恬、汹、录、扣、潋、滟、亦、抹、置、载、循、昙、秉、秧、砸、忌、膑、瞪、惑、讥、讽、蔑、序、率、瑜、遣、渡、策、滔、眺、幔、遮、苇、硫、磺、缆、盔、骼、椎、颌、趾、炙、烤、栎、羚、鸵、椭、政、瞬、琥、珀、蝇、脂、掸、拭、辣、渗、澎、湃、黏、测、伍、坨、啸、劣、酷、袭、僵、倚、秃、塑、豹、覆、莹、援、妄、诡、溃、扯、撤、瓢、褐、聋、疯、委、钧 召、狈、赴、伦、典、纳、喻、曼、庸、捅、刁、罚、释、绪、漏、抑、榜、寄、噢、杈、监、笛、嗡、惧、凄、憋、惩、樱、狱。　　小学六年级常用汉字生字　　厦、伐、综、砚、锤、焚、协、檐、汇、泽、宣、钮、徐、瞻、帜、袖、挪、谋、辈、脉、漓、澜、瑕、翡、峦、障、筏、绵、凌、酿、剥、妥、帖、藉、偿、馋、媒、诞、埃、渺、矿、赐、慷、慨、滥、睹、礴、丸、岷、咨、询、浏、碟、晖、迪、誉、篇、版、皱、歧、谨、巢、梢、暇、茸、甸、屑、掷、俗、瑞、兆、谚、枕、馒、柜、锈、摩、爹、袄、筒、揪、粥、吉、炕、叙、肤、签、缩、脾、册、熄、唉、欠、谅、俱、矣、曰、盂、沧、汤、阀、娱、僻、怖、宪、胖、刑、押、舅、绞、彻 迁、鸿、旺、标、炊、葬、权、溅、嘹、魅、萍、敏、滨、拆、申、挠、控、嘲、毅、扛、绘、桨、岔、竣、藐、蔡、羹、煎、诸、妒、督、寨、擂、呐、丞、璧、臣、诺、廉、颇、御、侮、辱、袍、荆、祭、乃、涕、洛、碗、伶、俐、徘、徊、裸、兜、蜷、焰、烘、哎、梗、填、橱、烁、魂、搁、帐、怨、掀、寡、揍、魁、霉、勺、熬、鼎、铸、铭、湛、昌、溶、构、寓、矛、盾、誉、吾、履、遂。　　小学各年级汉字生字　　范、刹、镶、裹、泻、镀、润、杰、截、溢、则、燃、缘、溜、货、奏、衡、诵、杖、超　　肌、拘、耽、误、哲、仪、悼、逝、餐、枣、搞、冠、骂、嚼、悟、摊、奥、咳、嗽、拄　　槐、耐、挽、饰、腮、洲、陪、检、阅、矫、锁、暂、糕、阻、谊、捣、谣、侦、混、吵　　耗、嫂、勒、骏、限、鞭、驰、蹄、茶、貌、杯、跤、尼、艇、叉、耸、寂、梁、寇、晋　　挥、葛、尸、悬、崖、斩、磨、罢、豪、屈、沃、刘、龄、匪、拒、醉、剂、施、哼、晕　　勉、堪、承、吟、残、瑟、捏、扭、胯、郑、拜、租、允、厨、颈、缚、稻、贼、畜、鲸　　肢、滤、吨、肺、判、胎、宅、蔽、弃、慎、址、掘、搜、骤、糙、朴、燥、钳、嘻、舀　　掺、逗、棍、逮、萝、卜、筷、哗、哇、托、批、糟、蹋、尔、肆、痰、核、呻、究、律`
            font_list = font_list.replace(/[　\s、。,，？?]/g, '').split('');
            let font_list_length = font_list.length;
        </script>
        <script>
            let query = {};
            location.search.split('?').pop().split('&').forEach(v => {
                if (v) {
                    let arr = v.split('=');
                    query[arr[0]] = arr[1];
                }
            });
            let num = Math.max(2, Math.min(10, +query.num || 5));
            let padding = 10;
            let width = Math.min(500, window.screen.width);
            
            let widthGezi = (width - padding * 2) / num;
            let fontSize = widthGezi * 0.6;
            let cavnas = document.getElementById("canvas");
            let ctx = cavnas.getContext("2d");

            let tools = document.getElementById('tools');
            let mask = document.getElementById('mask');
            let form_num = document.getElementById('num');
            let form_type = document.getElementById('type');
            let btn_create = document.getElementById('create');
            let btn_save = document.getElementById('save');

            for (let i = 2; i<=10; i++) {
                let opt = new Option(i, i);
                if (i == num) {
                    opt.selected = true;
                }
                form_num.options.add(opt);
            }

            if (query.type == 'hanzi') {
                form_type.options[1].selected = true;
            }

            cavnas.onclick = function () {
                tools.style.display = 'block';
                mask.style.display = 'block';
            }
            mask.onclick = function () {
                tools.style.display = 'none';
                mask.style.display = 'none';
            }
            btn_create.onclick = function () {
                let num = form_num.value;
                let type = form_type.value;

                let arr = [];
                if (num) {
                    arr.push('num=' + num);
                }
                if (type) {
                    arr.push('type=' + type);
                }

                let query = arr.join('&');

                location.href = location.protocol + '//' + location.host + location.pathname + (query ? '?' + query : '');
            }

            if (width < 500) {
                btn_save.onclick = function save() {
                    let imgData = canvas.toDataURL();
                    var xhr = null;
                    try {
                        xhr = new XMLHttpRequest();
                    } catch (e) {
                        xhr = new ActiveXObject("Microsoft.XMLHTTP");
                    }
                    //2.调用open方法（true----异步）
                    xhr.open("POST", "./upload", true);
                    //3.发送数据
                    let formData = new FormData()
                    formData.append('image', imgData);
                    //4.请求状态改变事件
                    xhr.onreadystatechange = function () {
                        if (xhr.readyState == 4) {
                            if (xhr.status == 200) {
                                let res = JSON.parse(xhr.responseText);
                                const anchor = document.createElement('a');
                                anchor.href = res.img;
                                anchor.download = res.img.split('/').pop();

                                anchor.click();
                            } else {
                                alert("错误" + xhr.status)
                            }
                        }
                    }
                    xhr.send(formData);
                }
            } else {
                btn_save.onclick = function save() {
                    canvas.toBlob(function (blob) {
                        const objectURL = URL.createObjectURL(new Blob([blob], { type: "image/png" }));

                        const anchor = document.createElement('a');
                        anchor.href = objectURL;
                        anchor.download = new Date().getTime()+".png";

                        anchor.click();

                        setTimeout(() => {
                            URL.revokeObjectURL(objectURL);
                        }, 5000);
                    }, "image/png", 1);
                }
            }

            let content_cache = {};
            function getNumber(weishu = 2) {
                while(true) {
                    let res = '';
                    for (let j = 0; j < weishu; j++) {
                        res += Math.floor(Math.random()*10);
                    }

                    if (!content_cache[res]) {
                        content_cache[res] = 1;
                        return res;
                    }
                }
            }

            function getHanzi() {
                while(1) {
                    let res = font_list[Math.floor(Math.random() * font_list_length)];
                    if (!content_cache[res]) {
                        content_cache[res] = 1;
                        return res;
                    }
                }
            }

            let getContext = query.type == 'hanzi'? getHanzi: getNumber;

            cavnas.width = width;
            canvas.height = width;
            ctx.fillStyle = '#ffffff';
            ctx.fillRect(0, 0, width, width);

            ctx.strokeStyle = '#000000';
            ctx.lineWidth = 2;
            ctx.fillStyle = '#000000';
            ctx.font = "bolder " + fontSize + "px Arial";
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            // 画线
            for (let i = 0; i<= num; i++) {
                let y = padding + i*widthGezi;
                ctx.moveTo(padding, y);
                ctx.lineTo(width - padding, y);

                let x = padding + i* widthGezi;
                ctx.moveTo(x, padding);
                ctx.lineTo(x, width - padding);
            }

            ctx.stroke();

            for (let i = 0; i < num; i++) {
                let x = padding + i*widthGezi + widthGezi/2;
                for (let i2 = 0; i2 < num; i2++) {
                    let y = padding + i2 * widthGezi + widthGezi / 2;

                    ctx.fillText(getContext(), x, y)
                }
            }
        </script>
    </body>
</html>